﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using Hive.Core.Common.Collections;
using Hive.Core.Common.Shell;

namespace Hive.Core.Model
{
    /// <summary>
    /// A binary repository that consists of ZIP archives of package exports.
    /// </summary>
    public class ArchivedBinaryRepository : IBinaryRepository
    {
        private readonly ArchivedBinaryRepositoryConfiguration configuration;

        /// <summary>
        /// Creates an archived binary repository.
        /// </summary>
        /// <param name="configuration">The configuration.</param>
        /// <exception cref="ArgumentNullException">Thrown if <paramref name="configuration"/> is null.</exception>
        public ArchivedBinaryRepository(ArchivedBinaryRepositoryConfiguration configuration)
        {
            if (configuration == null)
                throw new ArgumentNullException("configuration");

            this.configuration = configuration;
        }

        /// <inheritdoc />
        public BinaryRepositoryConfiguration Configuration
        {
            get { return configuration; }
        }

        /// <inheritdoc />
        public IBinaryArtifact GetArtifact(string packageName, string branchName, string versionLabel, IShell shell)
        {
            if (packageName == null)
                throw new ArgumentNullException("packageName");
            if (branchName == null)
                throw new ArgumentNullException("branchName");
            if (versionLabel == null)
                throw new ArgumentNullException("versionLabel");
            if (shell == null)
                throw new ArgumentNullException("shell");

            try
            {
                FileInfo archiveFile = new FileInfo(configuration.GetArchivePath(packageName, branchName, versionLabel));
                return new ArchivedBinaryArtifact(this, archiveFile, packageName, branchName, versionLabel);
            }
            catch (BinaryRepositoryException)
            {
                throw;
            }
            catch (OperationCanceledException)
            {
                throw;
            }
            catch (Exception ex)
            {
                throw new BinaryRepositoryException(string.Format("Failed to get binary artifact version '{0}' for branch '{1}' of package '{2}'.", versionLabel, branchName, packageName), ex);
            }
        }

        /// <inheritdoc />
        public IList<IBinaryArtifact> GetArtifacts(string packageName, string branchName, IShell shell)
        {
            if (packageName == null)
                throw new ArgumentNullException("packageName");
            if (branchName == null)
                throw new ArgumentNullException("branchName");
            if (shell == null)
                throw new ArgumentNullException("shell");

            try
            {
                // Note: The "*" is a bit of a hack and assumes that the version label is part of the filename itself.
                string archivePath = Path.GetFullPath(configuration.GetArchivePath(packageName, branchName, "*"));
                string archiveFileNamePattern = Path.GetFileName(archivePath);
                DirectoryInfo archiveDirectory = new DirectoryInfo(Path.GetDirectoryName(archivePath));

                if (!archiveDirectory.Exists)
                    return EmptyArray<IBinaryArtifact>.Instance;

                Regex versionLabelRegex = GetVersionLabelRegex(packageName, branchName);

                FileInfo[] archiveFiles = archiveDirectory.GetFiles(archiveFileNamePattern);
                return archiveFiles.Select(x => CreateBinaryArtifactFromFile(x, packageName, branchName, versionLabelRegex)).ToArray();
            }
            catch (BinaryRepositoryException)
            {
                throw;
            }
            catch (OperationCanceledException)
            {
                throw;
            }
            catch (Exception ex)
            {
                throw new BinaryRepositoryException(string.Format("Failed to enumerate the binary artifacts for branch '{0}' of package '{1}'.", branchName, packageName), ex);
            }
        }

        private Regex GetVersionLabelRegex(string packageName, string branchName)
        {
            string archivePath = Path.GetFullPath(configuration.GetArchivePath(packageName, branchName, "*"));
            return new Regex(Regex.Escape(archivePath).Replace(@"\*", "(.*)"));
        }

        private IBinaryArtifact CreateBinaryArtifactFromFile(FileInfo file, string packageName, string branchName, Regex versionLabelRegex)
        {
            Match match = versionLabelRegex.Match(file.FullName);
            Debug.Assert(match.Success, "Was unable to parse the version label from the file path.", file.FullName);

            string versionLabel = match.Groups[0].Value;
            return new ArchivedBinaryArtifact(this, file, packageName, branchName, versionLabel);
        }
    }
}
